/* ============================================================================== 
2  * NET Framework：4.0.30319.18408
3  * 创 建 者：$projectname$.qd01.zhouwei 
4  * 创建日期：2014/9/24 9:12:35  
5  * 功能说明：
6  * 引用说明：
* ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection.Emit;
using System.Data.Common;
using System.Reflection;
namespace TEmit
{
     class EntityBuilderIDataReader
    {
        private delegate T mapEntity<T>(DbDataReader dr);
        private static Dictionary<Type, Delegate> cachedMappers = new Dictionary<Type, Delegate>();

        public static IEnumerable<T> MapToEntities<T>(DbDataReader dr)
        {
            // If a mapping function from dr -> T does not exist, create and cache one
            if (!cachedMappers.ContainsKey(typeof(T)))
            {
                // Our method will take in a single parameter, a DbDataReader
                Type[] methodArgs = { typeof(DbDataReader) };

                // The MapDR method will map a DbDataReader row to an instance of type T
                DynamicMethod dm = new DynamicMethod("MapDR", typeof(T), methodArgs, Assembly.GetExecutingAssembly().GetType().Module);
                ILGenerator il = dm.GetILGenerator();

                // We'll have a single local variable, the instance of T we're mapping
                il.DeclareLocal(typeof(T));

                // Create a new instance of T and save it as variable 0
                il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
                il.Emit(OpCodes.Stloc_0);

                foreach (PropertyInfo pi in typeof(T).GetProperties())
                {
                    // Load the T instance, SqlDataReader parameter and the field name onto the stack
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldstr, pi.Name);

                    // Push the column value onto the stack
                    il.Emit(OpCodes.Callvirt, typeof(DbDataReader).GetMethod("get_Item", new Type[] { typeof(string) }));

                    // Depending on the type of the property, convert the datareader column value to the type
                    switch (pi.PropertyType.Name)
                    {
                        case "Int16":
                            il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt16", new Type[] { typeof(object) }));
                            break;
                        case "Int32":
                            il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt32", new Type[] { typeof(object) }));
                            break;
                        case "Int64":
                            il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt64", new Type[] { typeof(object) }));
                            break;
                        case "Boolean":
                            il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToBoolean", new Type[] { typeof(object) }));
                            break;
                        case "String":
                            il.Emit(OpCodes.Callvirt, typeof(string).GetMethod("ToString", new Type[] { }));
                            break;
                        case "DateTime":
                            il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToDateTime", new Type[] { typeof(object) }));
                            break;
                        case "Decimal":
                            il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToDecimal", new Type[] { typeof(object) }));
                            break;
                        default:
                            // Don't set the field value as it's an unsupported type
                            continue;
                    }

                    // Set the T instances property value
                    il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("set_" + pi.Name, new Type[] { pi.PropertyType }));
                }

                // Load the T instance onto the stack
                il.Emit(OpCodes.Ldloc_0);

                // Return
                il.Emit(OpCodes.Ret);

                // Cache the method so we won't have to create it again for the type T
                cachedMappers.Add(typeof(T), dm.CreateDelegate(typeof(mapEntity<T>)));
            }

            // Get a delegate reference to the dynamic method
            mapEntity<T> invokeMapEntity = (mapEntity<T>)cachedMappers[typeof(T)];

            // For each row, map the row to an instance of T and yield return it
            while (dr.Read())
                yield return invokeMapEntity(dr);
        }

        public static void ClearCachedMapperMethods()
        {
            cachedMappers.Clear();
        }
    }

    public class EntityBuilderIDataReader<Entity>
    {
        private delegate Entity Load(IDataReader dataReader);
        private Load handler;

        private EntityBuilderIDataReader() { }

        public Entity Build(IDataReader reader)
        {
            MethodInfo _me = handler.Method;
            _me.ToString();
            return handler(reader);
        }
        public static EntityBuilderIDataReader<Entity> CreateBuilder(IDataRecord reader)
        {
            EntityBuilderIDataReader<Entity> result = new EntityBuilderIDataReader<Entity>();
            DynamicMethod method = new DynamicMethod("CreateBuilder", typeof(Entity), new Type[] { typeof(IDataReader) }, typeof(Entity), true);
            ILGenerator generator = method.GetILGenerator();
            generator.DeclareLocal(typeof(Entity));
            generator.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc_0);
            MethodInfo getValue = reader.GetType().GetMethod("get_Item", new Type[] { typeof(int) });
            for (int i = 0; i < reader.FieldCount; i++)
            {

                PropertyInfo propertyInfo = typeof(Entity).GetProperty(reader.GetName(i));
                if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                {
                    Label label = generator.DefineLabel();
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, reader.GetType().GetMethod("IsDBNull"));
                    generator.Emit(OpCodes.Brtrue, label);
                    generator.Emit(OpCodes.Ldloc_0);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, getValue);
                    generator.Emit(OpCodes.Unbox_Any, reader.GetFieldType(i));
                    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                    generator.MarkLabel(label);
                }
            }
            generator.Emit(OpCodes.Ldloc_0);
            generator.Emit(OpCodes.Ret);
            result.handler = (Load)method.CreateDelegate(typeof(Load));
            return result;
        }
    }
}

